#include "BestQuoteTable.h"

#include <bondlib/ABBondPriceInfo.h>
#include <bondlib/BondCDCPriceInfo.h>
#include <bondlib/BondCSIPriceInfo.h>
#include <bondlib/BondCalendar.h>
#include <bondlib/BondSpecialLabel.h>
#include <bondlib/QBSortVContainer.h>
#include <bondlib/SSSortInfo.h>
#include <bondlib/SSVContainer.h>
#include <core/StyleMgr.h>
#include <include/CmdDefines.h>
#include <qbtools/tools.h>
#include <uam/ServerTimeMgr.h>
#include <uam/UserAccountManager.h>
#include <widgets/SImageButton.h>
#include <widgets/STabCtrl.h>
#include <widgets/table/STable.h>
#include <widgets/table/STableColumnSet.h>

#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QHeaderView>
#include <QMenu>
#include <QTableView>
#include <QVBoxLayout>

#include "../BondColDelegate.h"
#include "../BondDataCenter.h"
#include "../BondHeaderView.h"
#include "../BondUtility.h"
#include "../Detail/BondDetailDlg.h"
#include "../GridImage.h"
#include "../QbBondModule.h"
#include "../SSBizTool.h"
#include "../StringResource.h"
#include "BestPriceFormat.h"
#include "BestQuoteDefines.h"
#include "BestQuoteDlg.h"
#include "BestQuoteModel.h"
#include "BrokerContact.h"
#include "SubSpecialLabelDlg.h"

BestQuoteTable::BestQuoteTable(BestQuoteDlg* parent)
    : QFrame(parent), m_pParent(parent) {
  QString style =
      QString(
          "BestQuoteTable "
          "{background-color:#%1;border:none;border-radius:2px;}")
          .arg(StyleMgr::instance().color("BasicPlate").rgb(), 0, 16);
  setStyleSheet(style);
  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  setupUi();

  connect(m_pParent, &BestQuoteDlg::filterChange, this,
          &BestQuoteTable::onFilterChange);

  m_index = new CBondInfo;
  m_broker = new BrokerKey;

  QTimer::singleShot(100, [=]() { initUi(); });
}

BestQuoteTable::~BestQuoteTable() {
  onCheckOutMixedPriceTab(true);

  for (auto m : m_quoteModels) delete m.second;
  m_quoteModels.clear();
}

void BestQuoteTable::ReloadData() {
  m_selRowKey.clear();
  lockList(false);
  getBondList(nullptr);
  updateTable(true);
}

BestQuoteModel* BestQuoteTable::currentModel() {
  if (!m_brokerTab) return nullptr;

  int selTab = m_brokerTab->getSelTab();
  auto iter = m_quoteModels.find(selTab);
  if (iter != m_quoteModels.end())
    return iter->second;
  else {
    int brokerId = m_brokerTab->tabData(selTab).toInt();
    if (0 == brokerId) {
      CBrokerUnit stUnit;
      stUnit.m_brokerID = 0;
      stUnit.m_brokerName = "全部";  //"全部";
      stUnit.m_brokerENName = "All";
      stUnit.m_enableFlag_BOND = true;
      stUnit.m_enableFlag_NCD = true;

      CSortInfoManager sortInfoMng;
      sortInfoMng.InitData();
      sortInfoMng.SetSortCorp("");  // all:全部
      m_quoteModels[m_brokerTab->getSelTab()] =
          new BestQuoteModel(stUnit, sortInfoMng);

      return m_quoteModels[m_brokerTab->getSelTab()];
    } else if (QB_BROKER_BESTQUOTE == brokerId) {
      CBrokerUnit stUnit;
      stUnit.m_brokerID = QB_BROKER_BESTQUOTE;
      stUnit.m_brokerName =
          NewStringTable::strDlgQuote(64).toLocal8Bit().data();  //"最优";
      stUnit.m_brokerENName = "Best";
      stUnit.m_enableFlag_BOND = true;
      stUnit.m_enableFlag_NCD = true;
      initMixedBroker(stUnit);

      return m_quoteModels[m_brokerTab->getSelTab()];
    } else {
      const CBrokerList& brokerList =
          SSUserAccountManager::instance().GetReadonlyBroker();
      for (auto itor = brokerList.GetBrokerMap().begin();
           itor != brokerList.GetBrokerMap().end(); itor++) {
        const CBrokerUnit& stUnit = itor->second;
        if (stUnit.m_brokerID == brokerId) {
          CSortInfoManager sortInfoMng;
          sortInfoMng.InitData();
          sortInfoMng.SetSortCorp(
              QString::number(brokerId).toStdString());  // all:全部
          m_quoteModels[m_brokerTab->getSelTab()] =
              new BestQuoteModel(stUnit, sortInfoMng);

          return m_quoteModels[m_brokerTab->getSelTab()];
        }
      }
    }
  }

  return nullptr;
}

BestQuoteModel* BestQuoteTable::lastModel() {
  if (!m_brokerTab) return nullptr;

  int selTab = m_brokerTab->getLastSelTab();
  auto iter = m_quoteModels.find(selTab);
  if (iter != m_quoteModels.end())
    return iter->second;
  else {
    int brokerId = m_brokerTab->tabData(selTab).toInt();
    if (0 == brokerId) {
      CBrokerUnit stUnit;
      stUnit.m_brokerID = 0;
      stUnit.m_brokerName = "全部";  //"全部";
      stUnit.m_brokerENName = "All";
      stUnit.m_enableFlag_BOND = true;
      stUnit.m_enableFlag_NCD = true;

      CSortInfoManager sortInfoMng;
      sortInfoMng.InitData();
      sortInfoMng.SetSortCorp("");  // all:全部
      m_quoteModels[m_brokerTab->getSelTab()] =
          new BestQuoteModel(stUnit, sortInfoMng);

      return m_quoteModels[m_brokerTab->getSelTab()];
    } else if (QB_BROKER_BESTQUOTE == brokerId) {
      CBrokerUnit stUnit;
      stUnit.m_brokerID = QB_BROKER_BESTQUOTE;
      stUnit.m_brokerName =
          NewStringTable::strDlgQuote(64).toLocal8Bit().data();  //"最优";
      stUnit.m_brokerENName = "Best";
      stUnit.m_enableFlag_BOND = true;
      stUnit.m_enableFlag_NCD = true;
      initMixedBroker(stUnit);

      return m_quoteModels[m_brokerTab->getSelTab()];
    } else {
      const CBrokerList& brokerList =
          SSUserAccountManager::instance().GetReadonlyBroker();
      for (auto itor = brokerList.GetBrokerMap().begin();
           itor != brokerList.GetBrokerMap().end(); itor++) {
        const CBrokerUnit& stUnit = itor->second;
        if (stUnit.m_brokerID == brokerId) {
          CSortInfoManager sortInfoMng;
          sortInfoMng.InitData();
          sortInfoMng.SetSortCorp(
              QString::number(brokerId).toStdString());  // all:全部
          m_quoteModels[m_brokerTab->getSelTab()] =
              new BestQuoteModel(stUnit, sortInfoMng);

          return m_quoteModels[m_brokerTab->getSelTab()];
        }
      }
    }
  }

  return nullptr;
}

void BestQuoteTable::onFilterChange() { ReloadData(); }

void BestQuoteTable::onBestPriceReqArrivedAll() {
  if (isMixedBPTab()) {
    return;
  }

  m_bBestPriceUpdated = true;
  ReloadData();
}

void BestQuoteTable::onMixedBestPriceReqArrived() {
  BestQuoteModel* model = currentModel();
  if (!model) return;

  const CBrokerUnit& stUnit = model->GetBroker();
  if (stUnit.m_brokerID != QB_BROKER_BESTQUOTE) {
    return;
  }

  getMixedBondInfoList(&model->GetFilter());
  updateTable(false);
}

void BestQuoteTable::onMixedBestPricePush(
    QList<xQBMixedBestQuoteUnit_c> lstData) {
  BestQuoteModel* model = currentModel();
  if (!model) return;

  const CBrokerUnit& stUnit = model->GetBroker();
  if (stUnit.m_brokerID != QB_BROKER_BESTQUOTE) {
    return;
  }

  if (isMixedBPPushNeedRefresh(lstData)) {
    for (auto& mbp : lstData) {
      int bindex = CBondContainer::instance().GetBondIndex(mbp.m_bondkey,
                                                           mbp.m_listedmarket);
      if (bindex >= 0) {
        BrokerKey key("0", bindex);
        if (model->IsExist(key)) {
          QString key = QString("%1.%2").arg(0).arg(bindex);
          FlashInfo fi;
          fi.entireRow = true;
          fi.id = key;
          m_quoteTable->addFlashInfo(fi, false);
        }
      }
    }

    if (m_locked) {
      updateMixedBestPriceList(lstData, true);
      updateTable(false);
      m_quoteTable->showLockNewData("出现新报价");
    } else {
      updateMixedBestPriceList(lstData, false);
      updateTable(true);
    }
  }
}

void BestQuoteTable::onBestPriceArrivedAll(QList<xQBBestPriceUnit_c> bps) {
  BestQuoteModel* model = currentModel();
  if (!model || bps.isEmpty()) return;
  int compandId = atoi(bps.at(0).m_company_id);
  if (model->GetBroker().m_brokerID != 0 &&
      model->GetBroker().m_brokerID != compandId)
    return;

  FilterCondition stFC = model->GetFilter();
  bool hasData = model->CheckExistBestPricePush(
      stFC.stBlh, std::list<xQBBestPriceUnit_c>(bps.begin(), bps.end()));
  if (hasData) {
    for (auto& bp : bps) {
      int bindex = CBondContainer::instance().GetBondIndex(bp.m_bondkey,
                                                           bp.m_listedmarket);
      if (bindex >= 0) {
        BrokerKey key(bp.m_company_id, bindex);
        if (model->IsExist(key)) {
          QString key = QString("%1.%2").arg(bp.m_company_id).arg(bindex);
          FlashInfo fi;
          fi.entireRow = true;
          fi.id = key;
          m_quoteTable->addFlashInfo(fi, false);
        }
      }
    }

    if (!m_locked) {
      if (stFC.strSearch == "") {
        bool bGroup = false;
        if (stFC.m_nFocusType == 2) bGroup = true;
        if (bGroup) {
          /**TODO
          std::string groupid = MAKE_GBK(stFC.strGroupID);
          for (std::list<xQBBestPriceUnit_c>::iterator it =
          qbReq->m_List.begin(); it != qbReq->m_List.end(); ++it)
          {
                  if (CBondAttention().IsAttention(it->m_bondkey,
          it->m_listedmarket, groupid))
                  {
                          return true;//无论报价是否有效，关注组中的债券都要显示
                  }
          }
          */
        } else
          model->UpdateBestPricePush(stFC.stBlh, std::list<xQBBestPriceUnit_c>(
                                                     bps.begin(), bps.end()));
      } else {
        // TODO:
      }

      updateTable(true);

    } else {
      updateTable(false);
    }

    m_quoteTable->startFlash();
  }

  if (hasData && m_locked) {
    m_quoteTable->showLockNewData("出现新报价");
  }
}

void BestQuoteTable::onHeaderSort(int col) { sortHeader(col, false); }

void BestQuoteTable::onBestPriceRowClicked(const QModelIndex& index) {
  if (nullptr == m_quoteTable) {
    return;
  }

  lockList(true);

  QString key = index.data(DataIdRole).toString();
  int pos = key.indexOf(".");
  if (pos == -1) return;

  int bondIndex = key.mid(pos + 1).toInt();
  if (!CBondContainer::instance().IsValidIndex(bondIndex)) return;
  const CBondInfo& bond = CBondContainer::instance().ElementAtR(bondIndex);

  BestQuoteModel* bm = currentModel();
  if (!bm) return;

  BrokerKey brokerKey;
  int mouseRow = index.row();
  bm->GetRowMap().GetBondKey(mouseRow, brokerKey);
  m_selected_key = brokerKey;

  if (QB_BROKER_BESTQUOTE == bm->GetBroker().m_brokerID) {
    int pageCount = m_quoteTable->pageCount();
    int pos = m_quoteTable->sliderPos();
    m_quoteTable->setRealDataCount(bm->GetRowCount());

    CBrokerKeyVector brokerKeys;
    brokerKeys.reserve(pageCount);
    bm->GetScreenKeys(pos, pageCount, brokerKeys);

    int nCorpID = -1;
    if (mouseRow >= 0 && mouseRow < (int)brokerKeys.size()) {
      BrokerKey brokerkey = brokerKeys[mouseRow];
      nCorpID = atoi(brokerkey.GetBrokerId().c_str());
      if (nCorpID == 0) {
        mouseRow = -1;
      }
    }

    expandBond(brokerKey);
    updateTable(false, mouseRow, brokerKey.m_nIndex);
  } else {
    updateTable(false, mouseRow);
  }
}

void BestQuoteTable::onBestPriceRowDblClicked(const QModelIndex& index) {
  QString key = index.data(DataIdRole).toString();
  int pos = key.indexOf(".");
  if (pos == -1) return;

  int bondIndex = key.mid(pos + 1).toInt();
  if (!CBondContainer::instance().IsValidIndex(bondIndex)) return;
  const CBondInfo& bond = CBondContainer::instance().ElementAtR(bondIndex);

  BestQuoteModel* bm = currentModel();
  if (!bm) return;

  BrokerKey brokerKey;
  int mouseRow = index.row();
  bm->GetRowMap().GetBondKey(mouseRow, brokerKey);

  int col = index.column();
  if (col == COL_BIDVOL || col == COL_BIDFULLPRICE || col == COL_BIDNETPRICE ||
      col == COL_BID || col == COL_OFR || col == COL_OFRNETPRICE ||
      col == COL_OFRFULLPRICE || col == COL_OFRVOL) {
    int nId = atoi(brokerKey.GetBrokerId().c_str());
    OpenContactWindows(bondIndex, nId);
    return;
  }

  if (bond.IsValidBond()) {
    QbBondModule::instance().app()->execCommand(CMD_BondDetail,
                                                bond.GetCombBondKey());

    IMainFrame* frame = QbBondModule::instance().app()->mainFrame();
    int tabId = frame->existCmdWindow(CMD_BondDetail, bond.GetCombBondKey());
    if (tabId != -1) {
      QWidget* widget = nullptr;
      widget = frame->cmdWindow(tabId);

      BondDetailDlg* ptr = dynamic_cast<BondDetailDlg*>(widget);
      ptr->setBroker(atoi(brokerKey.GetBrokerId().c_str()));

      frame->activateCmdWindow(tabId);
    }
  }
}

void BestQuoteTable::setupUi() {
  QVBoxLayout* mainLayout = new QVBoxLayout(this);
  mainLayout->setContentsMargins(0, 4, 0, 0);
  mainLayout->setSpacing(4);

  m_brokerTab = BondUtility::createBrokerTab(this);
  if (m_brokerTab) {
    QHBoxLayout* layoutTab = new QHBoxLayout(this);
    layoutTab->setContentsMargins(4, 0, 4, 0);
    layoutTab->addSpacerItem(
        new QSpacerItem(60, 24, QSizePolicy::Fixed, QSizePolicy::Fixed));
    layoutTab->addSpacerItem(
        new QSpacerItem(0, 24, QSizePolicy::Expanding, QSizePolicy::Fixed));
    layoutTab->addWidget(m_brokerTab, 0, Qt::AlignCenter);
    layoutTab->addSpacerItem(
        new QSpacerItem(0, 24, QSizePolicy::Expanding, QSizePolicy::Fixed));
    m_btnSetting =
        new SImageButton(this, "guiframe_button_setting", "", tr("设置"));
    m_btnSetting->setImgAlign(Qt::AlignLeft);
    m_btnSetting->setColor(StyleMgr::instance().color("ButtonNormal"),
                           StyleMgr::instance().color("Hover"),
                           StyleMgr::instance().color("ButtonPressed"),
                           StyleMgr::instance().color("ButtonDisable"));
    m_btnSetting->setFixedSize(60, 24);
    connect(m_btnSetting, &SImageButton::clicked, this, [this](int) {
      SubSpecialLabelDlg dlg;
      dlg.exec();
    });
    layoutTab->addWidget(m_btnSetting);

    mainLayout->addLayout(layoutTab);
    m_brokerTab->selectTab(0);
  }

  m_quoteTable =
      new SVirtualTable(this, new BondHeaderView(Qt::Horizontal, this));
  m_model = new DefaultTableModel(this);
  m_model->setTable(m_quoteTable);
  mainLayout->addWidget(m_quoteTable);

  m_quoteTable->setHeaderSortable(true);
  m_quoteTable->table()->setModel(m_model);
  m_quoteTable->table()->horizontalHeader()->setSectionResizeMode(
      QHeaderView::Interactive);
  m_quoteTable->table()->horizontalHeader()->setSectionsMovable(true);
  m_quoteTable->table()->horizontalHeader()->show();
  m_quoteTable->table()->setContextMenuPolicy(Qt::CustomContextMenu);

  initTable();
  onBrokerSelChanged();

  RemainDateDelegate* bestQuoteDelegate =
      new RemainDateDelegate(m_quoteTable, m_tableSet, BottomCursor);
  m_quoteTable->table()->setItemDelegateForColumn(COL_TTM, bestQuoteDelegate);

  BrokerCorpDelegate* brokerCorpDelegate = new BrokerCorpDelegate(m_quoteTable);
  m_quoteTable->table()->setItemDelegateForColumn(COL_CORP, brokerCorpDelegate);

  if (m_tableSet->isColVisible2(COL_AB_PRICE)) {
    m_abTimer = new QTimer(this);
    m_abTimer->callOnTimeout(this, [this]() { onABTimer(); });
    m_abTimer->setInterval(1000 * 10);
    StartABTimer();
  }

  connect(m_tableSet, &STableColumnSet::tableSet, this,
          &BestQuoteTable::onTableSet);
  connect(m_quoteTable->table(), &QTableView::customContextMenuRequested, this,
          &BestQuoteTable::onContextMenu);

  connect(m_quoteTable, &SVirtualTable::headerSort, this,
          &BestQuoteTable::onHeaderSort);
  connect(m_quoteTable->table(), &QTableView::clicked, this,
          &BestQuoteTable::onBestPriceRowClicked);
  connect(m_quoteTable->table(), &QTableView::doubleClicked, this,
          &BestQuoteTable::onBestPriceRowDblClicked);
  connect(m_quoteTable, &SVirtualTable::unlocked, [=]() {
    lockList(false);
    updateTable(true);
  });
}

void BestQuoteTable::initTable() {
#define ALIGN_RIGHT (Qt::Alignment)(Qt::AlignVCenter | Qt::AlignRight)
#define ALIGN_LEFT (Qt::Alignment)(Qt::AlignVCenter | Qt::AlignLeft)
#define ALIGN_CENTER (Qt::Alignment)(Qt::AlignVCenter | Qt::AlignCenter)

  m_tableSet = new STableColumnSet(m_quoteTable, "BestPriceQuote", this);
  m_tableSet->setSaveCfg(true);

  BEGIN_COLUMN;
  ADD_COLUMN(0, m_tableSet, "", 0, 25, 25, 0.01, false, ALIGN_RIGHT, kFixed,
             "");
  m_cols.push_back(COL_FLAG);

  if (strcmp(SSUserAccountManager::instance().GetGlobleVariabl(
                 SSUserAccountManager::ShowBrokerFlag),
             "1") != 0) {
    ADD_COLUMN(COL_CORP, m_tableSet,
               NewStringTable::strDlgQuoteListHeadV2(COL_CORP), 0, 68, 12, 0.09,
               true, ALIGN_LEFT, kAuto, "");
    m_cols.push_back(COL_CORP);
  } else {
    ADD_COLUMN(COL_CORP, m_tableSet,
               NewStringTable::strDlgQuoteListHeadV2(COL_CORP), 0, 68, 12, 0.09,
               false, ALIGN_LEFT, kAuto, "");
    m_cols.push_back(COL_CORP);
  }

  ADD_COLUMN(COL_TTM, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_TTM), 0, 100, 12, 0.09,
             false, ALIGN_LEFT, kAuto, "");
  m_cols.push_back(COL_TTM);
  ADD_COLUMN(COL_CODE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CODE), 0, 120, 12, 0.12,
             false, ALIGN_LEFT, kAuto, "");
  m_cols.push_back(COL_CODE);
  ADD_COLUMN(COL_NAME, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_NAME), 0, 150, 12, 0.16,
             false, ALIGN_LEFT, kAuto,
             tr("特殊标签设置及详解，请点击右侧设置"));
  m_cols.push_back(COL_NAME);
  ADD_COLUMN(COL_TKN_CLEANPRICE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_TKN_CLEANPRICE), 1, 75,
             12, 0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_TKN_CLEANPRICE);

  //	ADD_COLUMN(COL_BIDCORP, m_tableSet,
  //NewStringTable::strDlgQuoteListHeadV2(COL_BIDCORP), 1, 68, 12, 0.09, false,
  //ALIGN_RIGHT, kAuto, ""); 	m_cols.push_back(COL_BIDCORP);

  ADD_COLUMN(COL_BIDVOL, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BIDVOL), 1, 100, 12,
             0.09, false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BIDVOL);
  ADD_COLUMN(COL_BIDFULLPRICE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BIDFULLPRICE), 1, 100,
             12, 0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BIDFULLPRICE);
  ADD_COLUMN(COL_BIDNETPRICE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BIDNETPRICE), 1, 100, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BIDNETPRICE);
  ADD_COLUMN(COL_BID, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BID), 1, 100, 40, 0.09,
             false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BID);
  ADD_COLUMN(COL_OFR, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_OFR), 1, 100, 40, 0.09,
             false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_OFR);
  ADD_COLUMN(COL_OFRNETPRICE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_OFRNETPRICE), 1, 100, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_OFRNETPRICE);
  ADD_COLUMN(COL_OFRFULLPRICE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_OFRFULLPRICE), 1, 100,
             12, 0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_OFRFULLPRICE);
  ADD_COLUMN(COL_OFRVOL, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_OFRVOL), 1, 100, 12,
             0.09, false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_OFRVOL);

  //	ADD_COLUMN(COL_OFRCORP, m_tableSet,
  //NewStringTable::strDlgQuoteListHeadV2(COL_OFRCORP), 1, 100, 12, 0.09, false,
  //ALIGN_RIGHT, kAuto, ""); 	m_cols.push_back(COL_OFRCORP);
  ADD_COLUMN(COL_MIXEDBP_UPDATE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_MIXEDBP_UPDATE), 1, 80,
             12, 0.09, false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_MIXEDBP_UPDATE);

  ADD_COLUMN(COL_COUPON_RATE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_COUPON_RATE), 0, 100, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_COUPON_RATE);
  ADD_COLUMN(COL_COUPON, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_COUPON), 0, 100, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_COUPON);
  ADD_COLUMN(COL_CDCVAL_YESTERDAY, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CDCVAL_YESTERDAY), 2,
             100, 12, 0.11, false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CDCVAL_YESTERDAY);
  ADD_COLUMN(COL_CDCVAL_TODAY, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CDCVAL_TODAY), 2, 100,
             12, 0.11, false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CDCVAL_TODAY);
  ADD_COLUMN(COL_DEAL_CDC, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_DEAL_CDC), 2, 80, 12,
             0.11, false, ALIGN_RIGHT, kAuto,
             tr("成交价：取每家经纪商的最新成交价"));
  m_cols.push_back(COL_DEAL_CDC);
  ADD_COLUMN(COL_CDC_CLEANPRICE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CDC_CLEANPRICE), 2, 80,
             12, 0.11, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CDC_CLEANPRICE);
  ADD_COLUMN(COL_DURATION, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_DURATION), 2, 72, 12,
             0.09, false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_DURATION);
  ADD_COLUMN(COL_ISSUER_RATE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_ISSUER_RATE), 0, 75, 12,
             0.09, false, ALIGN_LEFT, kAuto, "");
  m_cols.push_back(COL_ISSUER_RATE);
  ADD_COLUMN(COL_BOND_RATE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BOND_RATE), 0, 75, 12,
             0.09, false, ALIGN_LEFT, kAuto, "");
  m_cols.push_back(COL_BOND_RATE);
  ADD_COLUMN(COL_CDC_RATING, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CDC_RATING), 2, 75, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CDC_RATING);
  ADD_COLUMN(COL_CSI_RATING, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CSI_RATING), 2, 75, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CSI_RATING);
  ADD_COLUMN(COL_CBR_RATING, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CBR_RATING), 2, 75, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CBR_RATING);
  ADD_COLUMN(COL_OUTLOOK, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_OUTLOOK), 0, 53, 12,
             0.08, false, ALIGN_LEFT, kAuto, "");
  m_cols.push_back(COL_OUTLOOK);
  ADD_COLUMN(COL_CSIVAL, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CSIVAL), 2, 75, 12, 0.11,
             true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CSIVAL);
  ADD_COLUMN(COL_DEAL_CSI, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_DEAL_CSI), 2, 80, 12,
             0.11, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_DEAL_CSI);

  ADD_COLUMN(COL_CSI_CLEANPRICE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CSI_CLEANPRICE), 2, 80,
             12, 0.11, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CSI_CLEANPRICE);
  ADD_COLUMN(COL_PLEDGE_RATIO, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_PLEDGE_RATIO), 0, 80, 12,
             0.11, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_PLEDGE_RATIO);
  ADD_COLUMN(COL_OPTION_TYPE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_OPTION_TYPE), 0, 80, 12,
             0.11, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_OPTION_TYPE);
  ADD_COLUMN(COL_OPTION_DATE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_OPTION_DATE), 0, 80, 12,
             0.11, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_OPTION_DATE);
  ADD_COLUMN(COL_MATURITY_DATE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_MATURITY_DATE), 0, 80,
             12, 0.11, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_MATURITY_DATE);
  ADD_COLUMN(COL_TERM_TO_MATURITY, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_TERM_TO_MATURITY), 0, 80,
             12, 0.11, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_TERM_TO_MATURITY);
  ADD_COLUMN(COL_CSI_CDC, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CSI_CDC), 2, 95, 12,
             0.11, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CSI_CDC);
  ADD_COLUMN(COL_CROSSMARKET, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CROSSMARKET), 0, 60, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CROSSMARKET);
  ADD_COLUMN(COL_INDUSTRY, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_INDUSTRY), 0, 80, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_INDUSTRY);

  ADD_COLUMN(COL_RATINGAGENCY, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_RATINGAGENCY), 0, 94, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_RATINGAGENCY);
  ADD_COLUMN(COL_BOND_GUARANTOR, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BOND_GUARANTOR), 0, 94,
             12, 0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BOND_GUARANTOR);
  ADD_COLUMN(COL_BOND_LIQUIDITY, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BOND_LIQUIDITY), 0, 94,
             12, 0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BOND_LIQUIDITY);
  ADD_COLUMN(COL_ISSUER_LIQUIDITY, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_ISSUER_LIQUIDITY), 0, 94,
             12, 0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_ISSUER_LIQUIDITY);
  ADD_COLUMN(COL_OUTSTANDING_AMOUNT, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_OUTSTANDING_AMOUNT), 0,
             94, 12, 0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_OUTSTANDING_AMOUNT);
  ADD_COLUMN(COL_ACTUAL_ISSUE_AMOUNT, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_ACTUAL_ISSUE_AMOUNT), 0,
             94, 12, 0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_ACTUAL_ISSUE_AMOUNT);
  ADD_COLUMN(COL_ISSUE_TYPE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_ISSUE_TYPE), 0, 94, 12,
             0.09, true, ALIGN_LEFT, kAuto, "");
  m_cols.push_back(COL_ISSUE_TYPE);
  ADD_COLUMN(COL_MUNICIPAL_BOND, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_MUNICIPAL_BOND), 0, 94,
             12, 0.09, true, ALIGN_LEFT, kAuto, "");
  m_cols.push_back(COL_MUNICIPAL_BOND);
  ADD_COLUMN(COL_BOND_TYPE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BOND_TYPE), 0, 94, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BOND_TYPE);
  ADD_COLUMN(COL_BOND_ISSUER, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BOND_ISSUER), 0, 94, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BOND_ISSUER);
  ADD_COLUMN(COL_BID_CDCVAL, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BID_CDCVAL), 2, 100, 12,
             0.09, false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BID_CDCVAL);
  ADD_COLUMN(COL_CDCVAL_OFR, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CDCVAL_OFR), 2, 100, 12,
             0.09, false, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CDCVAL_OFR);

  ADD_COLUMN(COL_BID_CSIVAL, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_BID_CSIVAL), 2, 100, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_BID_CSIVAL);
  ADD_COLUMN(COL_CSIVAL_OFR, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_CSIVAL_OFR), 2, 100, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_CSIVAL_OFR);

  ADD_COLUMN(COL_AB_PRICE, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_AB_PRICE), 2, 100, 12,
             0.09, true, ALIGN_RIGHT, kAuto, "");
  m_cols.push_back(COL_AB_PRICE);

  ADD_COLUMN(COL_UPDATETIME, m_tableSet,
             NewStringTable::strDlgQuoteListHeadV2(COL_UPDATETIME), 0, 80, 12,
             0.09, false, ALIGN_CENTER, kAuto, "");
  m_cols.push_back(COL_UPDATETIME);

  m_tableSet->apply();
}

void BestQuoteTable::updateTable(bool reset, int selRow, int selBondIndex) {
  m_quoteTable->clearTable(reset);

  BestQuoteModel* bm = currentModel();
  if (!bm) return;

  bool isMixedTab = isMixedBPTab();
  int pageCount = m_quoteTable->pageCount();
  int pos = reset ? 0 : m_quoteTable->sliderPos();
  m_quoteTable->setRealDataCount(bm->GetRowCount());

  CBrokerKeyVector brokerKeys;
  brokerKeys.reserve(pageCount);
  bm->GetScreenKeys(pos, pageCount, brokerKeys);
  bm->GetRowMap().Clear();
  m_takens.clear();

  QFont f = font();
  f.setPixelSize(14);
  f.setWeight(QFont::Normal);
  //	f.setBold(true);

  QFont timeF = f;
  timeF.setPixelSize(14);

  const FilterCondition& filterCondition = bm->GetFilter();
  std::string contributorId = getContributorId(filterCondition);
  int realCount = brokerKeys.size();
  bool checkSpeed = false;
  for (int row = 0; row < realCount; ++row) {
    BrokerKey& brokerkey = brokerKeys[row];
    int nCorpID = atoi(brokerkey.GetBrokerId().c_str());  // stUnit.m_brokerID;
    int bondIndex = brokerkey.m_nIndex;

    const CBondInfo& bondInfo =
        CBondContainer::instance().ElementAtR(bondIndex);

    REPORT rpt, report;
    memset(&rpt, 0, sizeof(REPORT));
    memset(&report, 0, sizeof(REPORT));
    bool isDetail = false;
    if (isMixedTab && nCorpID == 0) {
      CBondContainer::instance().GetBrokerMixedBPRepById(
          bondIndex, contributorId.c_str(), rpt);
      CSSVContainer::RebuildReportBySettlement(filterCondition.stBlh, rpt,
                                               report, true);
    } else {
      CBondContainer::instance().GetCompanyRepById(
          bondIndex, brokerkey.GetBrokerId().c_str(), rpt);
      CSSVContainer::RebuildReportBySettlement(filterCondition.stBlh, rpt,
                                               report, false);
      if (isMixedTab) isDetail = true;
    }

    bool bidValid = strcmp(report.m_bidinfo.m_price_status, "0") != 0;
    bool ofrValid = strcmp(report.m_askinfo.m_price_status, "0") != 0;

    bool isRatingBond = bondInfo.IsRatingDebt();  // 利率债
    bool isExchange = bondInfo.IsExchange();      // 交易所券
    bool hasCDCAuth = bondInfo.IsCDCAuthValid();

    for (size_t col = 0; col < m_cols.size(); ++col) {
      if (!m_tableSet->isColVisible(col)) continue;

      QStandardItem* item = m_model->item(row, col);
      if (item == nullptr) item = new QStandardItem();

      item->clearData();

      EPriceColType colType = m_cols[col];
      if (COL_TTM == colType || COL_CODE == colType || COL_NAME == colType) {
        item->setData(StyleMgr::instance().color("TabSelBorder"),
                      Qt::ForegroundRole);
      } else {
        item->setData(StyleMgr::instance().color("TextNormal"),
                      Qt::ForegroundRole);
      }

      item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                    Qt::TextAlignmentRole);

      if (row % 2 == 0) {
        item->setData(StyleMgr::instance().color("BackNormal"),
                      Qt::BackgroundRole);
      } else {
        item->setData(StyleMgr::instance().color("BasicPlate"),
                      Qt::BackgroundRole);
      }

      item->setData(f, Qt::FontRole);
      QString idStr = QString("%1.%2").arg(nCorpID).arg(bondIndex);
      item->setData(idStr, DataIdRole);

      if (selRow != -1 || selBondIndex != -1) {
        if (isMixedTab) {
          if (isDetail && row == selRow) {
            m_selRowKey = idStr;
            item->setData(idStr, DataSelIdRole);
          } else if (!isDetail && selBondIndex == bondIndex) {
            m_selRowKey = idStr;
            item->setData(idStr, DataSelIdRole);
          }
        } else if (row == selRow) {
          m_selRowKey = idStr;
          item->setData(idStr, DataSelIdRole);
        }
      } else if (!m_selRowKey.isEmpty()) {
        item->setData(m_selRowKey, DataSelIdRole);
      } else {
        item->setData("", DataSelIdRole);
      }

      switch (colType) {
        case COL_FLAG:
          break;
        case COL_TTM: {
          if (isDetail) break;
          QString txt = QString::fromLocal8Bit(
              bondInfo.GetRemainPeriod(ServerTimeMgr::instance().serverTime()));
          item->setData(txt, Qt::DisplayRole);

          int holidayType = emHolidayNull;
          QString strHolidayDays;
          bool isCIB = true;
          int nDayCIB = 0, nDaySSE = 0;
          if (CBondCalendar::instance().IsHoliday_CHY(bondInfo, isCIB, nDayCIB,
                                                      nDaySSE)) {
            if (isCIB) {
              if (nDayCIB == nDaySSE)
                strHolidayDays = QString::number(nDayCIB);
              else
                strHolidayDays = QString("%1/%2").arg(nDayCIB).arg(nDaySSE);
              holidayType = emHolidayCib;
            } else {
              holidayType = emHolidaySSE;
              strHolidayDays = QString::number(nDaySSE);
            }
            item->setData(holidayType, HolidayTypeRole);  // 存休息类型
            item->setData(strHolidayDays, HolidayNumberRole);  // 存休息时间

            int maturityDay = atoi(bondInfo.GetMaturityDate());
            item->setData(maturityDay, MaturityDayRole);
          }
        } break;
        case COL_CODE: {
          if (isDetail) break;
          item->setData(QString::fromLocal8Bit(bondInfo.GetCombBondCode()),
                        Qt::DisplayRole);
        } break;
        case COL_NAME: {
          if (isDetail) break;
          QString txt = QString::fromLocal8Bit(bondInfo.GetBondShortName());
          if (strcmp(bondInfo.GetBondSubType(), "LLB") == 0) {
            if (strcmp(bondInfo.GetEntCor(), "SMB") == 0)
              txt += tr("(一般)");
            else if (strcmp(bondInfo.GetEntCor(), "NMB") == 0)
              txt += tr("(专项)");
          }

          QString specialLabel;
          refreshSpecialLabel(row, bondIndex, specialLabel);
          item->setData(txt, Qt::DisplayRole);
          item->setData(specialLabel, SpecialLabelRole);
        } break;
        case COL_TKN_CLEANPRICE: {
          int nPre = -1;
          QString tips;
          QString txt = SSTools::FormatePrice(
              getTakenPriceStr(row, brokerKeys, nPre, tips));
          if (txt.isEmpty()) {
            txt = "--";
            nPre = -1;
          }
          if (nPre != -1) {
            item->setData(BondUtility::gridImage((EnumImgFlag)nPre),
                          PrefixImgRole);
            item->setData(NewStringTable::strClean(), PrefixTipRole);
          }

          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_BIDVOL:
          BondUtility::DisplayVolumn(report.m_bidinfo, bidValid, false, item);
          break;
        case COL_BIDFULLPRICE:
        case COL_BIDNETPRICE: {
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          item->setData(StyleMgr::instance().color("Bid_GVN_H1"),
                        Qt::ForegroundRole);
          QString txt;
          if (bidValid) {
            if (colType == COL_BIDNETPRICE)
              txt = SSTools::FormatePrice(report.m_bidinfo.m_clean_price);
            else
              txt = SSTools::FormatePrice(report.m_bidinfo.m_full_price);
          }
          if (txt.isEmpty()) txt = "--";
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_BID:
          BondUtility::DisplayPriceNew(report.m_bidinfo, bidValid,
                                       StyleMgr::instance().color("Bid_GVN_H1"),
                                       false, false, false, false, checkSpeed,
                                       item);
          break;
        case COL_OFR:
          BondUtility::DisplayPriceNew(report.m_askinfo, ofrValid,
                                       StyleMgr::instance().color("Ofr_TKN_H1"),
                                       false, false, false, false, checkSpeed,
                                       item);
          break;
        case COL_OFRNETPRICE:
        case COL_OFRFULLPRICE: {
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          item->setData(StyleMgr::instance().color("Ofr_TKN_H1"),
                        Qt::ForegroundRole);
          QString txt;
          if (ofrValid) {
            if (colType == COL_OFRNETPRICE)
              txt = SSTools::FormatePrice(report.m_askinfo.m_clean_price);
            else
              txt = SSTools::FormatePrice(report.m_askinfo.m_full_price);
          }
          if (txt.isEmpty()) txt = "--";
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_OFRVOL:
          BondUtility::DisplayVolumn(report.m_askinfo, ofrValid, false, item);
          break;
        case COL_COUPON_RATE: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(bondInfo.GetCouponRateCurrent());
          int index = txt.indexOf(".");
          if (index > 0) txt = txt.left(index + 3);
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_COUPON: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(bondInfo.GetRateType());
          if (txt == "FIXED") {
            txt = tr("固息");  // TODO: ENg
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CDCVAL_YESTERDAY:
        case COL_CDCVAL_TODAY: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = "--";
          if (hasCDCAuth) {
            int optType = CBondCDCPriceInfo::est_type_invalid;
            if (colType == COL_CDCVAL_YESTERDAY)
              txt = QString::fromLocal8Bit(
                  CBondCDCPriceInfo::instance()
                      .GetCDCPriceYesterday(optType, hasCDCAuth, bondIndex,
                                            CBondCDCPriceInfo::price_type_yield,
                                            false)
                      .c_str());
            else
              txt = QString::fromLocal8Bit(
                  CBondCDCPriceInfo::instance()
                      .GetCDCPriceToday(optType, hasCDCAuth, bondIndex,
                                        CBondCDCPriceInfo::price_type_yield,
                                        false)
                      .c_str());
            if (txt.isEmpty()) txt = "--";

            // 中债估值 含权,显示三角形和tip
            switch (optType) {
              case CBondCDCPriceInfo::est_type_combine:
              case CBondCDCPriceInfo::est_type_exercise:
              case CBondCDCPriceInfo::est_type_expire: {
                item->setData(NewStringTable::strEstRemark(1 + optType),
                              Qt::ToolTipRole);
                item->setData(BondUtility::gridImage(grid_flag_triangle),
                              Qt::DecorationRole);
              } break;
              default:
                break;
            }
          }

          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_DEAL_CDC: {
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = BondUtility::getTakenCdcStr(
              getTakenPriceStr(row, brokerKeys, bondInfo), bondIndex,
              hasCDCAuth);
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CDC_CLEANPRICE:  // 中债净价
        {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          int optType = CBondCDCPriceInfo::est_type_invalid;
          QString txt = "--";
          txt = QString::fromLocal8Bit(
              CBondCDCPriceInfo::instance()
                  .GetCDCPriceYesterday(optType, hasCDCAuth, bondIndex,
                                        CBondCDCPriceInfo::price_type_clean,
                                        false)
                  .c_str());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_DURATION:  // 久期
        {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          int optType = CBondCDCPriceInfo::est_type_invalid;
          QString txt = "--";
          txt = QString::fromLocal8Bit(
              CBondCDCPriceInfo::instance()
                  .GetCDCPriceYesterday(optType, hasCDCAuth, bondIndex,
                                        CBondCDCPriceInfo::price_type_duration,
                                        false)
                  .c_str());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_ISSUER_RATE: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(bondInfo.GetIssuerRating());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_BOND_RATE:  // 主/债 评级
        {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(bondInfo.GetBondRating());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CDC_RATING:  // 中债隐含评级
        {
          if (isDetail) break;
          if (SSUserAccountManager::instance().HasAuthOfCDC()) {
            item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                          Qt::TextAlignmentRole);
            QString txt = "--";

            if (strlen(bondInfo.GetCDCRating()) == 0 ||
                strcmp(bondInfo.GetCDCRating(), "CCL") == 0) {
              txt = "--";
            } else {
              txt = QString::fromLocal8Bit(bondInfo.GetCDCRating());
            }

            item->setData(txt, Qt::DisplayRole);
          }
        } break;
        case COL_CSI_RATING:  // 中证隐含评级
        {
          if (isDetail) break;
          if (SSUserAccountManager::instance().HasAuthOfCSI()) {
            item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                          Qt::TextAlignmentRole);
            QString txt = "--";
            if (strlen(bondInfo.GetCSIRating()) == 0 ||
                strcmp(bondInfo.GetCSIRating(), "CCL") == 0) {
              txt = "--";
            } else {
              txt = QString::fromLocal8Bit(bondInfo.GetCSIRating());
            }

            item->setData(txt, Qt::DisplayRole);
          }
        } break;
        case COL_CBR_RATING:  // 中债资信评级
        {
          if (isDetail) break;
          if (SSUserAccountManager::instance().HasAuthOfCBR()) {
            item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                          Qt::TextAlignmentRole);
            QString txt = "--";
            if (strlen(bondInfo.GetCBRRating()) == 0 ||
                strcmp(bondInfo.GetCBRRating(), "CCL") == 0) {
              txt = "--";
            } else {
              txt = QString::fromLocal8Bit(bondInfo.GetCBRRating());
            }

            item->setData(txt, Qt::DisplayRole);
          }
        } break;
        case COL_OUTLOOK:  // 展望
        {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(bondInfo.GetOutlookName());
          if (txt.isEmpty() || txt.compare("无") == 0) {
            txt = "--";
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CSIVAL: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (SSUserAccountManager::instance().GetCSIAuthorityFlag()) {
            UINT nType = 0;
            txt = QString::fromLocal8Bit(
                qb::biz::csi::FmtYield(bondInfo.GetCombBondKey(), nType)
                    .c_str());
            if (txt.isEmpty()) {
              txt = "--";
            }

            switch (nType) {
              case CBondCSInfo::emCSITypeCombine:
              case CBondCSInfo::emCSITypeExercise: {
                item->setData(NewStringTable::strEstRemark(nType - 1),
                              Qt::ToolTipRole);
                item->setData(BondUtility::gridImage(grid_flag_triangle),
                              Qt::DecorationRole);
              } break;
              case CBondCSInfo::emCSITypeMaturity:
              default:
                break;
            }
          } else {
            txt = "";
          }

          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_DEAL_CSI: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (SSUserAccountManager::instance().GetCSIAuthorityFlag()) {
            txt = QString::fromLocal8Bit(
                qb::biz::csi::FmtDealMinusCSI(
                    bondInfo.GetCombBondKey(),
                    getTakenPriceStr(row, brokerKeys, bondInfo))
                    .c_str());
            if (txt.isEmpty()) txt = "--";
          } else {
            txt = "";
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CSI_CLEANPRICE: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (SSUserAccountManager::instance().GetCSIAuthorityFlag()) {
            txt = QString::fromLocal8Bit(
                qb::biz::csi::FmtCleanPrice(bondInfo.GetCombBondKey()).c_str());
            if (txt.isEmpty()) txt = "--";
          } else {
            txt = "";
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_PLEDGE_RATIO: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(bondInfo.GetConversionRate());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_OPTION_TYPE: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(bondInfo.GetOptionTypeName());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_OPTION_DATE: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          int nDate = atoi(bondInfo.GetOptionDate());
          if (nDate > 0) {
            txt = QString("%1-%2-%3")
                      .arg(nDate / 10000, 4, 10, QLatin1Char('0'))
                      .arg((nDate % 10000) / 100, 2, 10, QLatin1Char('0'))
                      .arg((nDate % 100), 2, 10, QLatin1Char('0'));
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_MATURITY_DATE:  // 到期日
        {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          int nDate = atoi(bondInfo.GetMaturityDate());
          if (nDate > 0) {
            txt = QString("%1-%2-%3")
                      .arg(nDate / 10000, 4, 10, QLatin1Char('0'))
                      .arg((nDate % 10000) / 100, 2, 10, QLatin1Char('0'))
                      .arg((nDate % 100), 2, 10, QLatin1Char('0'));
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_TERM_TO_MATURITY: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(bondInfo.GetTermToMaturity());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CSI_CDC: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (hasCDCAuth &&
              SSUserAccountManager::instance().GetCSIAuthorityFlag())
            txt = QString::fromLocal8Bit(
                qb::biz::csi::FmtCSIMinusCDC(&bondInfo, bondIndex, hasCDCAuth)
                    .c_str());
          else
            txt = "";
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CROSSMARKET: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (bondInfo.GetCrossmarket() == 'Y' ||
              bondInfo.GetCrossmarket() == 'y')
            txt = "是";
          else if (bondInfo.GetCrossmarket() == 'N' ||
                   bondInfo.GetCrossmarket() == 'n')
            txt = "否";
          else
            txt = QString(bondInfo.GetCrossmarket());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_INDUSTRY: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(bondInfo.GetSwSector());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_RATINGAGENCY: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt =
              QString::fromLocal8Bit(bondInfo.GetRatingInstitutionShortName());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_BOND_GUARANTOR: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(
              qb::biz::BuildGuarantorName(bondInfo.GetWarranter(), false)
                  .c_str());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_BOND_LIQUIDITY:
          break;
        case COL_ISSUER_LIQUIDITY:
          break;
        case COL_OUTSTANDING_AMOUNT: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          double fAmount = bondInfo.GetOutstandingAmount();
          QString txt;
          if (fAmount < 0.0001) {
            txt = "--";
          } else {
            txt = QString::number(fAmount / 100000, 'f', 2);
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_ACTUAL_ISSUE_AMOUNT: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          double fAmount = bondInfo.GetActualIssueAmount();
          QString txt;
          if (fAmount < 0.0001) {
            txt = "--";
          } else {
            txt = QString::number(fAmount / 10000, 'f', 2);
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_ISSUE_TYPE:  // 发行
        {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (!bondInfo.is_interest_bond() &&
              strcmp(bondInfo.GetAssetStatus(), "PRI") != 0 &&
              strcmp(bondInfo.GetAssetStatus(), "PUB") != 0) {
            txt = NewStringTable::strFilterAssetStatus(1);  // 公募
          } else if (strcmp(bondInfo.GetAssetStatus(), "PUB") == 0) {
            txt = NewStringTable::strFilterAssetStatus(2);  // 小公募
          } else if (strcmp(bondInfo.GetAssetStatus(), "PRI") == 0) {
            txt = NewStringTable::strFilterAssetStatus(3);  // 私募
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_MUNICIPAL_BOND:  // 地方债类型
        {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (strcmp(bondInfo.GetBondSubType(), "LLB") == 0 &&
              strcmp(bondInfo.GetEntCor(), "SMB") == 0) {
            txt = NewStringTable::strFilterLocalBond(1);  // 一般债
          } else if (strcmp(bondInfo.GetBondSubType(), "LLB") == 0 &&
                     strcmp(bondInfo.GetEntCor(), "NMB") == 0) {
            txt = NewStringTable::strFilterLocalBond(2);  // 专项债
          }
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_BOND_TYPE: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(
              qb::biz::BuildBondTypeName(bondInfo.GetBondSubType(),
                                         bondInfo.GetEntCor(), false)
                  .c_str());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_BOND_ISSUER: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignLeft | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt = QString::fromLocal8Bit(
              qb::biz::BuildIssuerName(bondInfo.GetIssuerCode(), false)
                  .c_str());
          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_BID_CDCVAL: {
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (hasCDCAuth) {
            txt = QString::fromLocal8Bit(
                GetBidEstPrice(report, &bondInfo, bondIndex, hasCDCAuth));
          } else {
            txt = "";
          }

          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CDCVAL_OFR: {
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (hasCDCAuth) {
            txt = QString::fromLocal8Bit(
                GetEstPriceOfr(report, &bondInfo, bondIndex, hasCDCAuth));
          } else {
            txt = "";
          }

          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_BID_CSIVAL: {
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (SSUserAccountManager::instance().GetCSIAuthorityFlag()) {
            txt = QString::fromLocal8Bit(
                qb::biz::csi::FmtBidMinusCSI(report, &bondInfo).c_str());
          } else {
            txt = "";
          }

          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CSIVAL_OFR: {
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (SSUserAccountManager::instance().GetCSIAuthorityFlag()) {
            txt = QString::fromLocal8Bit(
                qb::biz::csi::FmtCSIMinusOfr(report, &bondInfo).c_str());
          } else {
            txt = "";
          }

          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_AB_PRICE: {
          if (isDetail) break;
          item->setData((int)(Qt::AlignRight | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
          QString txt;
          if (CABBondPriceInfo::instance().GetPrice(bondIndex) != nullptr) {
            txt = CABBondPriceInfo::instance().GetPrice(bondIndex);
          }
          if (txt.isEmpty()) txt = tr("--");

          item->setData(txt, Qt::DisplayRole);
        } break;
        case COL_CORP: {
          if (bm->GetBroker().m_brokerID == 0) {
            int nCurBroker = atoi(report.m_company_id);
            if (CBrokerList::IsValidBroker(nCurBroker)) {
              item->setData(nCurBroker, BrokerIdRole);
              item->setData(Qt::AlignCenter, DataAlignRole);
            }
          } else if (bm->GetBroker().m_brokerID == QB_BROKER_BESTQUOTE) {
            if (isDetail) {
              item->setData(nCorpID, BrokerIdRole);
              item->setData(Qt::AlignRight, DataAlignRole);
            } else {
              item->setData(QB_BROKER_BESTQUOTE, BrokerIdRole);
              item->setData(Qt::AlignCenter, DataAlignRole);
            }
          }
        } break;
        case COL_MIXEDBP_UPDATE:
        case COL_UPDATETIME: {
          time_t t_Current = 0;
          t_Current = qMax(report.m_time, report.m_create_time);
          BondUtility::SetUpdateTimeCol(item, t_Current);
          item->setData(timeF, Qt::FontRole);
          item->setData((int)(Qt::AlignCenter | Qt::AlignVCenter),
                        Qt::TextAlignmentRole);
        } break;
        case COL_MAXCOUNT:
          break;
        default:
          break;
      }

      m_model->setItem(row, col, item);
    }
    bm->GetRowMap().Add(row, brokerkey);
  }
}

void BestQuoteTable::updateSpecialListWidth() {
  if (!m_tableSet) return;

  BestQuoteModel* model = currentModel();
  if (!model) return;

  const CBrokerUnit& stUnit = model->GetBroker();
  if (stUnit.m_brokerID == 0) {
    //	m_tableSet->setColumnHide(COL_BIDCORP, true);
    //	m_tableSet->setColumnHide(COL_OFRCORP, true);
    m_tableSet->setColumnHide(COL_MIXEDBP_UPDATE, true);
    m_tableSet->setColumnHide(COL_CORP, false);
    m_tableSet->setColumnHide(COL_UPDATETIME, false);
  } else if (stUnit.m_brokerID == QB_BROKER_BESTQUOTE) {
    m_tableSet->setColumnHide(COL_UPDATETIME, true);
    //	m_tableSet->setColumnHide(COL_BIDCORP, false);
    //	m_tableSet->setColumnHide(COL_OFRCORP, false);
    m_tableSet->setColumnHide(COL_MIXEDBP_UPDATE, false);
    m_tableSet->setColumnHide(COL_CORP, false);
  } else if (CBrokerList::IsValidBroker(stUnit.m_brokerID)) {
    //	m_tableSet->setColumnHide(COL_BIDCORP, true);
    //	m_tableSet->setColumnHide(COL_OFRCORP, true);
    m_tableSet->setColumnHide(COL_MIXEDBP_UPDATE, true);
    m_tableSet->setColumnHide(COL_CORP, true);
    m_tableSet->setColumnHide(COL_UPDATETIME, false);
  }
}

void BestQuoteTable::onBrokerSelChanged() {
  if (!m_tableSet) return;

  BestQuoteModel* model = currentModel();
  if (!model) return;

  const CBrokerUnit& stUnit = model->GetBroker();
  if (stUnit.m_brokerID == 0) {
    for (int i = 0; i < (int)m_cols.size(); ++i) {
      if (COL_UPDATETIME == m_cols[i]) {
        sortHeader(i, true, true);
        break;
      }
    }
  } else if (stUnit.m_brokerID == QB_BROKER_BESTQUOTE) {
    for (int i = 0; i < (int)m_cols.size(); ++i) {
      if (COL_MIXEDBP_UPDATE == m_cols[i]) {
        sortHeader(i, true, true);
        break;
      }
    }
  } else if (CBrokerList::IsValidBroker(stUnit.m_brokerID)) {
    for (int i = 0; i < (int)m_cols.size(); ++i) {
      if (COL_UPDATETIME == m_cols[i]) {
        sortHeader(i, true, true);
        break;
      }
    }
  }
}

void BestQuoteTable::StartABTimer() {
  QB_CHECK_RETURN_VOID1(m_abTimer);
  m_abTimer->start();
}

void BestQuoteTable::StopABTimer() {
  QB_CHECK_RETURN_VOID1(m_abTimer);
  m_abTimer->stop();
}

void BestQuoteTable::onABTimer() {
  if (m_bBestPriceUpdated)  // 确保最优报价已经更新
  {
    if (!m_tableSet->isColVisible2(COL_AB_PRICE)) {
      StopABTimer();
      return;
    }

    if (m_bFirstReqABBond) {
      BondDataCenter::instance().requestABBondPrice();
      StopABTimer();
      m_abTimer->setInterval(5 * 60 * 1000);
      StartABTimer();
      m_bFirstReqABBond = false;
    } else {
      BondDataCenter::instance().requestABBondPrice();
    }
  }
}

void BestQuoteTable::resizeEvent(QResizeEvent* event) {
  QFrame::resizeEvent(event);

  if (m_btnSetting && m_brokerTab &&
      m_brokerTab->geometry().right() + m_btnSetting->rect().width() >
          geometry().right()) {
    m_btnSetting->hide();
  } else {
    m_btnSetting->show();
  }

  updateSpecialListWidth();
}

void BestQuoteTable::sortHeader(int col, bool desc, bool reset) {
  BestQuoteModel* bm = currentModel();
  if (!bm) return;

  CSortInfoManager& sortInfoMng = bm->GetSortInfo();
  if (reset) {
    sortInfoMng.InitData();
  }

  int sortKey = getSortKey(m_cols[col]);
  if (sortKey != VBOND_NULL) {
    bool bPressCtrl =
        qApp->queryKeyboardModifiers().testFlag(Qt::ControlModifier);
    sortInfoMng.AddOrUpdate(bPressCtrl, sortKey, desc);

    DefaultTableModel* dataModel =
        dynamic_cast<DefaultTableModel*>(m_quoteTable->table()->model());
    for (size_t i = 0; i < m_cols.size(); ++i) {
      CSortInfo sortinfo;
      ColumnInfo ci = m_tableSet->colInfoByIndex(i);
      if (sortInfoMng.GetSortInfo(getSortKey(m_cols[i]), sortinfo)) {
        QString preFix = sortinfo.m_bDesc ? "↓ " : "↑ ";
        dataModel->setHeaderData(i, Qt::Horizontal, preFix + ci.name);
      } else
        dataModel->setHeaderData(i, Qt::Horizontal, ci.name);
    }

    if (sortInfoMng.GetSize() > 0) {
      m_selRowKey.clear();
      bm->Sort();
      updateTable(true);
    }
  }
}

bool BestQuoteTable::getCurrentCondition(FilterCondition* filter) {
  if (!filter || !m_pParent) return false;

  FilterCondition& fcTemp = m_pParent->getFilterCondition();
  filter->stBlh = fcTemp.stBlh;
  filter->nSearchType = fcTemp.nSearchType;
  filter->strSearch = fcTemp.strSearch;
  return true;
}

void BestQuoteTable::getBondList(FilterCondition* pFC) {
  BestQuoteModel* model = currentModel();
  if (!model) return;

  const CBrokerUnit& stUnit = model->GetBroker();

  FilterCondition stFC;
  FilterCondition& stTemp = model->GetFilter();
  if (stUnit.m_brokerID == QB_BROKER_BESTQUOTE) {
    return getMixedBondInfoList(pFC);
  }

  if (pFC == nullptr) {
    if (!getCurrentCondition(&stFC)) {
      // return;
    }
    stTemp = stFC;
  }

  std::list<std::string> brokerIds;
  bool allTab = model->GetBroker().m_brokerID == 0;
  if (allTab)  // all
  {
    int count = m_brokerTab->getTabCount();
    int index = 0;
    for (int i = 0; i < count; ++i) {
      int brokerId = m_brokerTab->tabData(i).toInt();
      if (brokerId == 0) continue;

      brokerIds.push_back(QString::number(brokerId).toStdString());
      stTemp.stBlh.nBrokId[index] = brokerId;
      ++index;
    }
  } else {
    brokerIds.push_back(
        QString::number(model->GetBroker().m_brokerID).toStdString());
    stTemp.stBlh.nBrokId[0] = model->GetBroker().m_brokerID;
    FMTBUF(stTemp.stBlh.sCompanyid, "%d", model->GetBroker().m_brokerID);
  }

  model->ReloadRecords(stTemp, brokerIds, allTab, "");
}

void BestQuoteTable::initUi() {
  m_selRowKey.clear();
  getBondList(nullptr);
  updateTable(true);

  connect(&BondDataCenter::instance(), &BondDataCenter::bestPriceArrivedAll,
          this, &BestQuoteTable::onBestPriceArrivedAll);
  connect(&BondDataCenter::instance(), &BondDataCenter::bestPriceReqArrivedAll,
          this, &BestQuoteTable::onBestPriceReqArrivedAll);
  connect(&BondDataCenter::instance(), &BondDataCenter::mixedBestPriceArrived,
          this, &BestQuoteTable::onMixedBestPriceReqArrived);
  connect(&BondDataCenter::instance(), &BondDataCenter::mixedBestPricePush,
          this, &BestQuoteTable::onMixedBestPricePush);
  connect(m_quoteTable, &SVirtualTable::viewPosChanged,
          [=](int) { updateTable(false); });
  connect(m_brokerTab, &STabCtrl::tabSelected, [=]() {
    lockList(false);

    if (lastModel() != nullptr &&
        QB_BROKER_BESTQUOTE == lastModel()->GetBroker().m_brokerID) {
      onCheckOutMixedPriceTab(true);
    }
    if (QB_BROKER_BESTQUOTE == currentModel()->GetBroker().m_brokerID) {
      onCheckInMixedPriceTab();
    }

    m_selRowKey.clear();
    getBondList(nullptr);
    updateTable(true);
    updateSpecialListWidth();
    onBrokerSelChanged();

    emit brokerChanged(m_brokerTab->getSelTab());
  });
}

static char s_empty_str[1] = "";

char* BestQuoteTable::getTakenPriceStr(int index,
                                       const CBrokerKeyVector& brokerKeys,
                                       const CBondInfo& bi) {
  if (m_takens.empty()) {
    if (isMixedBPTab()) {
      CMarketStream::bondStream().GetLastMarketStream(brokerKeys, m_takens);
    } else {
      CMarketStream::bondStream().GetMarketStreamByBrokers(brokerKeys,
                                                           m_takens);
    }
  }

  if (index >= 0 && index < (int)m_takens.size()) {
    MarketStreamInfo& info = m_takens[index];
    if (atoi(info.m_body.m_quotetype) == 3)
      return info.m_body.m_yield;
    else {
      if (bi.IsBondABS() || bi.IsTransBond()) {
        double ft = atof(info.m_body.m_yield);
        if (ft <= 0 || ft >= 30) return s_empty_str;
      }
      return info.m_body.m_yield;
    }
  }

  return s_empty_str;
}

char* BestQuoteTable::getTakenPriceStr(int index,
                                       const CBrokerKeyVector& brokerKeys,
                                       int& prcType, QString& szTips) {
  prcType = -1;
  if (m_takens.empty()) {
    if (isMixedBPTab()) {
      CMarketStream::bondStream().GetLastMarketStream(brokerKeys, m_takens);
    } else {
      CMarketStream::bondStream().GetMarketStreamByBrokers(brokerKeys,
                                                           m_takens);
    }
  }

  if (index >= 0 && index < (int)m_takens.size()) {
    MarketStreamInfo& info = m_takens[index];
    if (info.m_body.IsCleanPrice()) {
      prcType = grid_tag_netprice;
      szTips = NewStringTable::strClean();
    }  // 净价
    else if (info.m_body.IsFullPrice()) {
      prcType = grid_tag_fullprice;
      szTips = NewStringTable::strFull();
    }  // 全价
    return info.m_body.m_price;
  }

  return s_empty_str;
}

int BestQuoteTable::getSortKey(int nDefCol) {
  int sortKey = VBOND_NULL;
  switch (nDefCol) {
    case COL_TTM:
      sortKey = VEXPIRY_DATE;
      break;
    case COL_CODE:
      sortKey = VBOND_CODE;
      break;
    case COL_NAME:
      sortKey = VSHORT_NAME;  // TODO: ENG
      break;
    case COL_TKN_CLEANPRICE:
      sortKey = kTknPrice;
      break;
    case COL_BIDVOL:
      sortKey = isMixedBPTab() ? V_MIXEDBP_BUYVOL : VBUYVOL_SETTLEMENT;
      break;
    case COL_BIDFULLPRICE:
      sortKey =
          isMixedBPTab() ? V_MIXEDBP_BUYFULLPRICE : VBUYFULLPRICE_SETTLEMENT;
      break;
    case COL_BIDNETPRICE:
      sortKey =
          isMixedBPTab() ? V_MIXEDBP_BUYNETPRICE : VBUYNETPRICE_SETTLEMENT;
      break;
    case COL_BID:
      sortKey = isMixedBPTab() ? V_MIXEDBP_BUYPRICE : VBUYPRICE_SETTLEMENT;
      break;
    case COL_OFR:
      sortKey = isMixedBPTab() ? V_MIXEDBP_SELLPRICE : VSELLPRICE_SETTLEMENT;
      break;
    case COL_OFRNETPRICE:
      sortKey =
          isMixedBPTab() ? V_MIXEDBP_SELLNETPRICE : VSELLNETPRICE_SETTLEMENT;
      break;
    case COL_OFRFULLPRICE:
      sortKey =
          isMixedBPTab() ? V_MIXEDBP_SELLFULLPRICE : VSELLFULLPRICE_SETTLEMENT;
      break;
    case COL_OFRVOL:
      sortKey = isMixedBPTab() ? V_MIXEDBP_SELLVOL : VSELLVOL_SETTLEMENT;
      break;
    case COL_COUPON_RATE:
      sortKey = kCouponRate;
      break;
    case COL_COUPON:  //   票面   add  by  lbl
      sortKey = kRateType;
      break;
    case COL_CDCVAL_YESTERDAY:
      sortKey = VESTBPRICEPRE;
      break;
    case COL_CDCVAL_TODAY:
      sortKey = VESTBPRICETODAY;
      break;
    case COL_CDC_CLEANPRICE:
      sortKey = kCdcCleanPrice;
      break;
    case COL_DURATION:
      sortKey = kDuration;
      break;
    case COL_ISSUER_RATE:
      sortKey = VISSUERRATING;
      break;
    case COL_BOND_RATE:
      sortKey = VBONDRATING;
      break;
    case COL_CDC_RATING:
      sortKey = VCDCRATING;
      break;
    case COL_CSI_RATING:
      sortKey = VCSIRATING;
      break;
    case COL_CBR_RATING:
      sortKey = VCBRRATING;
      break;
    case COL_OUTLOOK:
      sortKey = kOutlook;
      break;
    case COL_CSIVAL:
      sortKey = kCsiVal;
      break;
    case COL_CSI_CLEANPRICE:
      sortKey = kCsiCleanPrice;
      break;
    case COL_PLEDGE_RATIO:
      sortKey = kConvRate;
      break;
    case COL_OPTION_TYPE:
      sortKey = kOptionType;
      break;
    case COL_OPTION_DATE:
      sortKey = kOptionDate;
      break;
    case COL_MATURITY_DATE:
      sortKey = kMaturityDate;
      break;
    case COL_TERM_TO_MATURITY:
      sortKey = TERM_TO_MATURITY;
      break;
    case COL_CSI_CDC:
      sortKey = kCsi_Cdc;
      break;
    case COL_CROSSMARKET:
      sortKey = kCrossMarket;
      break;
    case COL_INDUSTRY:
      sortKey = kIndustry;  // TODO ENG
      break;
    case COL_RATINGAGENCY:
      sortKey = kRatingAgency;
      break;
    case COL_BOND_TYPE:
      sortKey = VBondType;
      break;
    case COL_BOND_ISSUER:
      sortKey = VBondIssuer;
      break;
    case COL_BOND_GUARANTOR:
      sortKey = kBondGuarantor;  // 担保人 TODO ENG
      break;
    case COL_BOND_LIQUIDITY:
      sortKey = kBondLiquidity;
      break;
    case COL_ISSUER_LIQUIDITY:
      sortKey = kIssuerLiquidity;
      break;
    case COL_OUTSTANDING_AMOUNT:
      sortKey = kOutstandingAmount;
      break;
    case COL_ACTUAL_ISSUE_AMOUNT:
      sortKey = kActualIssureAmount;
      break;
    case COL_UPDATETIME:
      sortKey = VMODIFY_TIME_SETTLEMENT;
      break;
    case COL_MIXEDBP_UPDATE:
      sortKey = V_MIXEDBP_MODIFY_TIME;
      break;
    case COL_BID_CDCVAL:
      sortKey = isMixedBPTab() ? V_MIXEDBP_BID_CDC : kBid_EstPrice_Settlement;
      break;
    case COL_CDCVAL_OFR:
      sortKey = isMixedBPTab() ? V_MIXEDBP_CDC_OFR : kEstPrice_Ofr_Settlement;
      break;
    case COL_BID_CSIVAL:
      sortKey = isMixedBPTab() ? V_MIXEDBP_BID_CSI : kBid_CsiVal_Settlement;
      break;
    case COL_CSIVAL_OFR:
      sortKey = isMixedBPTab() ? V_MIXEDBP_CSI_OFR : kCsiVal_Ofr_Settlement;
      break;
    case COL_CORP:
      sortKey = VBROKERKEY;
      break;
    case COL_DEAL_CDC:
      sortKey = kDeal_Cdc;
      break;
    case COL_DEAL_CSI:
      sortKey = kDeal_Csi;
      break;
    case COL_ISSUE_TYPE:  // 发行
      sortKey = VBondIssueType;
      break;
    case COL_MUNICIPAL_BOND:  // 地方债类型
      sortKey = VMunicipalBond;
      break;
    case COL_AB_PRICE:
      sortKey = kABRecommendPrice;
      break;
    default:
      break;
  }

  return sortKey;
}

void BestQuoteTable::OpenContactWindows(int bondIndex, int brokerId) {
  BrokerContact a(this);

  a.UpdataContactList(brokerId, bondIndex);

  a.exec();
}

void BestQuoteTable::refreshSpecialLabel(int index, int bondIndex,
                                         QString& specialLabel) {
  const CBondInfo& bondInfo = CBondContainer::instance().ElementAtR(bondIndex);

  std::string tips;
  std::vector<bondSpecialLabel> vctSpecialLabel;
  std::vector<std::string> labelIDs =
      BondSpecialLabelMgr::instance().getLabelIDs(bondInfo.GetCombBondKey());
  for (size_t i = 0; i < labelIDs.size(); i++) {
    const std::string& id = labelIDs[i];
    tips = BondSpecialLabelMgr::instance().getLabelTips(
        bondInfo.GetCombBondKey(), id);
    const SpecialLabel& sl = SpecialLabelMgr::instance().get(id);
    if (sl.id().empty()) {
      continue;
    }

    bondSpecialLabel bsl;
    bsl.id = sl.id().c_str();
    bsl.label = QString::fromLocal8Bit(sl.label().c_str());
    bsl.order = sl.Order();
    vctSpecialLabel.push_back(bsl);
  }

  if ((int)vctSpecialLabel.size() > 0) {
    std::sort(vctSpecialLabel.begin(), vctSpecialLabel.end());

    for (int i = 0; i < (int)vctSpecialLabel.size(); ++i) {
      specialLabel.append(vctSpecialLabel[i].label);

      if (i != (int)vctSpecialLabel.size() - 1) {
        specialLabel.append("|");
      }
    }
  }

  // 成交-中债大于50bp，显示偏标签，这个比较特殊，由客户端实时计算
  /*	bool hasCDCAuth = bondInfo.IsCDCAuthValid();
          if (hasCDCAuth) {
                  int exec = -1;
                  const char* sTakenPrice = getTakenPriceStr(index, bondInfo,
     exec); string cdcStr = GetTakenCdcStr(sTakenPrice, bondIndex, hasCDCAuth,
     exec); if (cdcStr != "--") { double val = atof(cdcStr.c_str()); if (val >
     50) { const SpecialLabel& sl =
     SpecialLabelMgr::instance().get("001");//001为偏Id if (!sl.id().empty()) {
                                          std::string label = sl.label();
                                          int order = sl.Order();
                                  }
                          }
                  }
          }*/
}

bool BestQuoteTable::isSelectedBond() {
  return m_selected_key == BrokerKey() ? false : true;
}

void BestQuoteTable::lockList(bool bLock) {
  if (nullptr == m_quoteTable) {
    return;
  }

  m_locked = bLock;
  if (bLock) {
    m_quoteTable->lockTable();
  } else {
    m_selected_key = BrokerKey();
    m_quoteTable->unlockTable();
    m_quoteTable->unSelectItem();
  }
}

void BestQuoteTable::onTableSet() {
  if (m_bBestPriceUpdated)  // 确保最优报价已经更新
  {
    if (m_tableSet->isColVisible2(COL_AB_PRICE)) {
      if (nullptr == m_abTimer) {
        m_abTimer = new QTimer(this);
        m_abTimer->callOnTimeout(this, [this]() { onABTimer(); });
      }

      m_bFirstReqABBond = true;
      StopABTimer();
      m_abTimer->setInterval(1000 * 10);
      StartABTimer();
    } else {
      StopABTimer();
    }
  }
}

void BestQuoteTable::onContextMenu(QPoint pos) {
  QModelIndex index = m_quoteTable->table()->currentIndex();
  QString key = index.data(DataIdRole).toString();
  int pos1 = key.indexOf(".");
  if (pos1 == -1) return;

  int bondIndex = key.mid(pos1 + 1).toInt();
  *m_index = CBondContainer::instance().ElementAtR(bondIndex);

  BestQuoteModel* bm = currentModel();
  if (!bm) return;

  int mouseRow = index.row();

  bm->GetRowMap().GetBondKey(mouseRow, *m_broker);

  FormatPriceToClipBoardV2(m_broker->GetBrokerId().c_str(),
                           QString::fromLocal8Bit(m_index->GetCombBondKey()),
                           m_copyData, CSSVContainer::GetServerTime(), true,
                           false);

  QMenu* menu = new QMenu(this);
  menu->setAttribute(Qt::WA_DeleteOnClose);

  menu->addAction(tr("债券详情"), this, [=]() { onMenuClick(BondDetial); });

  QMenu* smallMenu = menu->addMenu(tr("弹出小窗"));
  smallMenu->setEnabled(false);
  smallMenu->addAction(tr("行情小窗"), this,
                       [=]() { onMenuClick(QuotationWindow); });
  smallMenu->addAction(tr("利差小窗"), this,
                       [=]() { onMenuClick(MarginWindow); });

  menu->addAction(tr("相似债券"), this, [=]() { onMenuClick(BondSimilar); });

  QMenu* calculatorMenu = menu->addMenu(tr("计算器"));
  calculatorMenu->setEnabled(false);
  calculatorMenu->addAction(tr("债券价格试算"), this,
                            [=]() { onMenuClick(TrialCalculatin); });
  calculatorMenu->addAction(tr("持有期收益"), this,
                            [=]() { onMenuClick(HoldCalculatin); });

  menu->addAction(tr("复制债券简称"), this,
                  [=]() { onMenuClick(CopyBondShortName); });
  menu->addAction(tr("复制价格"), this, [=]() { onMenuClick(CopyPrice); });

  QAction* action1 = new QAction(menu);
  action1->setText(tr("添加至默认关注"));
  action1->setEnabled(false);
  menu->addAction(action1);

  QAction* action2 = new QAction(menu);
  action2->setText(tr("添加至关注"));
  action2->setEnabled(false);
  menu->addAction(action2);

  QAction* action3 = new QAction(menu);
  action3->setText(tr("设置债券提醒"));
  action3->setEnabled(false);
  menu->addAction(action3);

  // menu->addAction(tr("添加至默认关注"), this, [=]()
  // {onMenuClick(AddDefaultFollow); }); menu->addAction(tr("添加至关注"), this,
  // [=]() {onMenuClick(AddFollow); }); menu->addAction(tr("设置债券提醒"),
  // this, [=]() {onMenuClick(SetBondReminder); });

  if (m_quoteTable->table()->isVisible()) {
    menu->popup(QCursor::pos());
  }
}

void BestQuoteTable::onMenuClick(tipsMenu index) {
  const CBondInfo& bond = *m_index;
  BrokerKey brokerKey = *m_broker;

  switch (index) {
    case BondDetial: {
      if (bond.IsValidBond()) {
        QbBondModule::instance().app()->execCommand(CMD_BondDetail,
                                                    bond.GetCombBondKey());

        IMainFrame* frame = QbBondModule::instance().app()->mainFrame();
        int tabId =
            frame->existCmdWindow(CMD_BondDetail, bond.GetCombBondKey());
        if (tabId != -1) {
          QWidget* widget = nullptr;

          widget = frame->cmdWindow(tabId);

          BondDetailDlg* ptr = dynamic_cast<BondDetailDlg*>(widget);
          ptr->setBroker(atoi(brokerKey.GetBrokerId().c_str()));

          frame->activateCmdWindow(tabId);
        }
      }
    } break;
    case QuotationWindow:
    case MarginWindow:
    case BondSimilar: {
      QString param = QString::fromLocal8Bit(bond.GetCombBondKey());
      param.append("|").append("RELATED");

      QbBondModule::instance().app()->execCommand(CMD_WebBondDetail_Profile,
                                                  param.toUtf8());
    } break;
    case TrialCalculatin:
    case HoldCalculatin:
    case CopyBondShortName: {
      QClipboard* pClip = QApplication::clipboard();
      pClip->clear();
      pClip->setText(QString::fromLocal8Bit(bond.GetBondShortName()));
    } break;
    case CopyPrice: {
      QClipboard* pClip = QApplication::clipboard();
      pClip->clear();
      pClip->setText(m_copyData);
    } break;
    case AddDefaultFollow:
    case AddFollow:
    case SetBondReminder:
    default:
      break;
  }
}